General description

This package allows an easy implementation of a client/server application using TCP/IP connections over a network. It has been successfully tested on the following systems :

This library implements the following features : Warning : the server works as a multi-threaded task ; thus you must develop reentrant functions (no static or global variables).

This package has been developped using a C++ compiler ; gcc on Unix systems and Microsoft Visual C++ 5.0 for NT.


Registration

This package is shareware, not freeware. If you do not register your version of these libraries :

Registration price :
France : 180 FF + 20,6% TVA
U.S.A. : 30 $
Other : 30 $ or equivalent

Number of licences : unlimited.
Upgrades : free
Assistance : an assistance can be provided.

You can contact me for any additionnal information about registration : Didier.Cassereau@espci.fr


Known problems

Some knows problems may require some specific configurations :


The XDR mechanism

The XDR mechanism allows to insure that the binary representation of data can be understood by the different clients and servers connected to the network, independantly of their own operating systems.
Each system implements some encoding and decoding functions that must be used before transmission (encoding) and after reception (decoding).

From these basic XDR functions, you can develop your own XDR routines for your own objects.
 


Compiling an application using this package

To use this package, you must have :

On Unix systems, you can write your makefile according to the following example. We suppose here that rpcdc.h is installed in a directory that can be accessed by the g++ compiler, and that rpcdc.a has been duplicated under the name librpcdc.a in a directory that can be accessed by the linker.

Linux system : the server must be linked with the pthread library

HP-UX system 10.xx : the server must be linked with the DCE package (must be installed on your system) HP-UX system 9.xx : no particular linker directive for the server Windows NT/95 :

This package has been tested with Microsoft Visual C++ 5.0 only.
Just create a project including your source files, and link your program with rpcdc.lib. Depending on your source code, you can have to specify an additional library wsock32.lib.
When running your application, be sure that your program can find rpcdc.dll !
 


Server example

Here is a listing of a simple server that is able to manage tcp and udp requests.
 

//-----------------------------------------------------------------------------
// This program illustrates a simple server implementation
// The server is listening on the tcp and udp ports
//-----------------------------------------------------------------------------

#ifdef WIN32
    #include <windows.h>
#endif
#include <stdio.h>
#include <math.h>

#include <rpcdc.h>

//-----------------------------------------------------------------------------
// Here is a definition of a structure that must be transmitted to the server
// One possibility is to develop a specific XDR function, used with transmit/
// receive functions
//-----------------------------------------------------------------------------
typedef struct _TEST {

    int i1;
    int i2;
    int i3;

    float f;
    double d;

} TEST;

//-----------------------------------------------------------------------------
// First write the XDR function - standard structure
// The parameters are as follows :
//      h : (void *) points to the host binary representation of the data
//      n : (void *) points to the network binary representation of the data
//      code : indicates the operation to perform (3 possibilities)
//              --> XDR_ENCODE : conversion h -> n
//              --> XDR_DECODE : conversion n -> h
//              --> XDR_MEMORY : just returns the size of the object
//-----------------------------------------------------------------------------
size_t _xdr_TEST (void *h, void *n, int code)

{
    TEST *hi=(TEST *)h;             // first convert pointers
    char *ni=(char *)n;
    size_t size;

    switch (code) {

    case XDR_ENCODE:
    case XDR_DECODE:

        size=_xdr_int(NULL,NULL,XDR_MEMORY);
        _xdr_int((void *)&hi->i1,(void *)ni,code);   ni+=size;
        _xdr_int((void *)&hi->i2,(void *)ni,code);   ni+=size;
        _xdr_int((void *)&hi->i3,(void *)ni,code);   ni+=size;

        size=_xdr_float(NULL,NULL,XDR_MEMORY);
        _xdr_float((void *)&hi->f,(void *)ni,code);  ni+=size;

        size=_xdr_double(NULL,NULL,XDR_MEMORY);
        _xdr_double((void *)&hi->d,(void *)ni,code); ni+=size;
        return 0;

    case XDR_MEMORY:
    default:
        return 3*_xdr_int(NULL,NULL,XDR_MEMORY)+
                 _xdr_float(NULL,NULL,XDR_MEMORY)+
                 _xdr_double(NULL,NULL,XDR_MEMORY);
    }
}

//-----------------------------------------------------------------------------
// Now the function to send a TEST structure becomes very simple !
// The following example returns 1 if success, 0 if a problem occurs.
// Binary conversion is automatically performed through the XDR routine
// written above.
// Warning : remember to call _rpc_flush() !
//-----------------------------------------------------------------------------
int _rpcsend_TEST (SOCKET Socket,
                   RPC_MSG *rpc_msg, XDR_BUFFER *xdr_buffer,
                   TEST *TestParameter)

{
    return _rpcsend_buffer(Socket,rpc_msg,xdr_buffer,TestParameter,_xdr_TEST)==
           _xdr_TEST(NULL,NULL,XDR_MEMORY);
}

//-----------------------------------------------------------------------------
// Similarly, the function to receive a TEST structure is also simple.
// Binary conversion is automatically performed through the XDR routine
// written above.
//-----------------------------------------------------------------------------
int _rpcrecv_TEST (SOCKET Socket,
                   XDR_BUFFER *xdr_buffer,
                   TEST *TestParameter)

{
    return _rpcrecv_buffer(Socket,xdr_buffer,TestParameter,_xdr_TEST)==
           _xdr_TEST(NULL,NULL,XDR_MEMORY);
}

//-----------------------------------------------------------------------------
// This is one of the most important functions : each time a message is
// received by the server, this function is called
// The first parameter rpc_func identifies the message
// The second parameter idClient identifies the client (the server can accept
// connections with multiple clients at the same time)
//-----------------------------------------------------------------------------
static void Dispatch (int rpc_func, int idClient)

{
    SOCKET socket;
    RPC_MSG *rpc_msg;
    XDR_BUFFER *xdr_buffer;
    char *client_buffer;
    int I;
    TEST Test;

// The first thing to do is to retrieve the parameters of the client
    _GetClientInfo(idClient,
                   &socket,
                   &rpc_msg,
                   &xdr_buffer,
                   &client_buffer);

// Now, the function reduces to a big switch depending of the value of rpc_func
    switch (rpc_func) {

// First kind of message (prefer constants defined by #define instead of numerical constants)
// This message requires to read additionnal parameters (int + TEST strcuture) from the socket
// The response of the server to the client : just add 1 to the int sent by the client, and then return it
// Similar changes to the TEST structure
// Note the call to _rpc_flush() !
    case 1:
        _rpcrecv_int(socket,xdr_buffer,&I);
        _rpcrecv_TEST(socket,xdr_buffer,&Test);
        printf("Data received from client : %d\n",I);
        printf("TEST structure            : %d %d %d %g %lg\n",Test.i1,Test.i2,Test.i3,Test.f,Test.d);
// Now change the values
        I++;
        Test.i1*=2;
        Test.i2*=3;
        Test.i3*=4;
        Test.f=1.0f/Test.f;
        Test.d=sqrt(Test.d);
// Now returns to changes parameters to client
        _rpcsend_int(socket,rpc_msg,xdr_buffer,I);
        _rpcsend_TEST(socket,rpc_msg,xdr_buffer,&Test);
        _rpc_flush(socket,rpc_msg);
        break;

// Other messages and associated code ...

    default:
        return;
    }
}

//-----------------------------------------------------------------------------
// This accepts or not a connection from one client on the network, whose ip
// address is transmitted as a character string parameter. The return value is
// 0 (connection refused) or 1 (connection accepted)
//-----------------------------------------------------------------------------
static int AcceptConnection (char *ip)

{
    return 1;
}

//-----------------------------------------------------------------------------
// This function is called each time a message is received on the udp socket
// of the server (if it exists).
// In this example, the server sends an answer made of one long int, followed
// by a 128 characters string.
//-----------------------------------------------------------------------------
void udpManager (SOCKET udpSocket)

{
    sockaddr_in src_addr;
    long int udpQuery;
    int stat,len;
    struct {
        long int udpResponse;
        char szb[128];
    } udp;

// First I read the data from the udp socket. In this example, the client is assumed to send
// only a 32 bits message
    len=sizeof(src_addr);
    stat=recvfrom(udpSocket,(char *)&udpQuery,sizeof(long int),0,(struct sockaddr *)&src_addr,&len);
    if (stat!=sizeof(long int)) return;
// Note the use of ntohl(), required by the XDR mechanism
    udpQuery=ntohl(udpQuery);

// Depending on the received message, I generate the answer, replying (udpQuery+1)
    switch (udpQuery) {
    case 1:
        udp.udpResponse=htonl(1+udpQuery);  // response=query+1
        strcpy(udp.szb,"udp query #1");     // text of the response
        break;
    default:
        udp.udpResponse=htonl(0);           // response=0
        strcpy(udp.szb,"");                 // empty text
    }

// Now send the response to the client who sent the udp request
    sendto(udpSocket,(char *)&udp,sizeof(udp),MSG_DONTROUTE,(struct sockaddr *)&src_addr,sizeof(sockaddr_in));
}

//-----------------------------------------------------------------------------
// This function starts the server with a specific service
//-----------------------------------------------------------------------------
static void Start (char *service)

{
    int stat;
    SOCKET tcpSocket,udpSocket;
    char msg[256];

    stat=_StartServerDaemon(service,
                            10,                 // timeout en réception : 60 secondes par défaut
                            "",                 // no log file
                            0,                  // no client data
                            NULL,               // Constructor=NULL (no client data)
                            NULL,               // Destructor=NULL (no client data)
                            Dispatch,           // Dispatch function written above
                            AcceptConnection,   // Acceptation function written above
                            udpManager,         // udpManager function written above
                            NULL,               // registration information
                            &tcpSocket,         // tcp socket created by this function if successful
                            &udpSocket);        // udp socket created by this function if successful

// An error has occured ! print the error message and leave.
    if (!stat) {
        _GetErrMsg(msg);
        printf("%s\n",msg);
        return;
    }

// Now start the infinite loop of the server
    _ServerMainLoop(NULL);

// The program should never reach this point !
    _StopServerDaemon(tcpSocket,udpSocket);
}

//-----------------------------------------------------------------------------
int main (int argc, char **argv)

{
    if (argc!=2) {
        printf("usage : %s <service>\n",argv[0]);
        return 0;
    }

    Start(argv[1]);

    return 0;
}
 


Client example

Here is a listing of a simple client that can be used with the server described above.
 

//-----------------------------------------------------------------------------
// This program illustrates a simple tcp/udp client implementation
//-----------------------------------------------------------------------------

#ifdef WIN32
    #include <windows.h>
#endif

#include <stdio.h>
#include <time.h>

#include <rpcdc.h>

//-----------------------------------------------------------------------------
// Here is a definition of a structure that must be transmitted to the server
// One possibility is to develop a specific XDR function, used with transmit/
// receive functions
//-----------------------------------------------------------------------------
typedef struct _TEST {

    int i1;
    int i2;
    int i3;

    float f;
    double d;

} TEST;

//-----------------------------------------------------------------------------
// First write the XDR function - standard structure
// The parameters are as follows :
//      h : (void *) points to the host binary representation of the data
//      n : (void *) points to the network binary representation of the data
//      code : indicates the operation to perform (3 possibilities)
//              --> XDR_ENCODE : conversion h -> n
//              --> XDR_DECODE : conversion n -> h
//              --> XDR_MEMORY : just returns the size of the object
//-----------------------------------------------------------------------------
size_t _xdr_TEST (void *h, void *n, int code)

{
    TEST *hi=(TEST *)h;             // first convert pointers
    char *ni=(char *)n;
    size_t size;

    switch (code) {

    case XDR_ENCODE:
    case XDR_DECODE:

        size=_xdr_int(NULL,NULL,XDR_MEMORY);
        _xdr_int((void *)&hi->i1,(void *)ni,code);   ni+=size;
        _xdr_int((void *)&hi->i2,(void *)ni,code);   ni+=size;
        _xdr_int((void *)&hi->i3,(void *)ni,code);   ni+=size;

        size=_xdr_float(NULL,NULL,XDR_MEMORY);
        _xdr_float((void *)&hi->f,(void *)ni,code);  ni+=size;

        size=_xdr_double(NULL,NULL,XDR_MEMORY);
        _xdr_double((void *)&hi->d,(void *)ni,code); ni+=size;
        return 0;

    case XDR_MEMORY:
    default:
        return 3*_xdr_int(NULL,NULL,XDR_MEMORY)+
                 _xdr_float(NULL,NULL,XDR_MEMORY)+
                 _xdr_double(NULL,NULL,XDR_MEMORY);
    }
}

//-----------------------------------------------------------------------------
// Now the function to send a TEST structure becomes very simple !
// The following example returns 1 if success, 0 if a problem occurs.
// Binary conversion is automatically performed through the XDR routine
// written above.
// Warning : remember to call _rpc_flush() !
//-----------------------------------------------------------------------------
int _rpcsend_TEST (SOCKET Socket,
                   RPC_MSG *rpc_msg, XDR_BUFFER *xdr_buffer,
                   TEST *TestParameter)

{
    return _rpcsend_buffer(Socket,rpc_msg,xdr_buffer,TestParameter,_xdr_TEST)==
           _xdr_TEST(NULL,NULL,XDR_MEMORY);
}

//-----------------------------------------------------------------------------
// Similarly, the function to receive a TEST structure is also simple.
// Binary conversion is automatically performed through the XDR routine
// written above.
//-----------------------------------------------------------------------------
int _rpcrecv_TEST (SOCKET Socket,
                   XDR_BUFFER *xdr_buffer,
                   TEST *TestParameter)

{
    return _rpcrecv_buffer(Socket,xdr_buffer,TestParameter,_xdr_TEST)==
           _xdr_TEST(NULL,NULL,XDR_MEMORY);
}

//-----------------------------------------------------------------------------
// tcp client implementation
//-----------------------------------------------------------------------------
void mainTCP (char *server, char *service)

{
    SOCKET Socket;              // to communicate with the server
    RPC_MSG rpc_msg;            // for transmission buffering
    XDR_BUFFER xdr_buffer;      // for XDR buffering
    char msg[256];
    int ans;
    time_t t;
    TEST Test;

    time(&t);

// Initialization of the TEST structure
    Test.i1=10;
    Test.i2=17;
    Test.i3=25;
    Test.f=3.14159f;
    Test.d=2.7182818;

// Connection request to the specified server, with the specified service
    Socket=_StartTCPClient(server,
                           service,
                           10,
                           &rpc_msg,
                           &xdr_buffer);

// An error occured, or the connection has been rejected
    if (Socket==0) {
        _GetErrMsg(msg);
        printf("%s\n",msg);
        return;
    }

// Now the connection has been accepted !

// The first int (value 1) corresponds to the first rpc message transmitted to the server
    _rpcsend_int(Socket,&rpc_msg,&xdr_buffer,1);
// This message also uses an additionnal parameter (int) : here the local time
    _rpcsend_int(Socket,&rpc_msg,&xdr_buffer,(int)t);
// The last part of the message is made of the TEST structure defined above
    _rpcsend_TEST(Socket,&rpc_msg,&xdr_buffer,&Test);
// flushing is required !
    _rpc_flush(Socket,&rpc_msg);

// Now I am waiting for the answer coming from the server !
// In this example, the first int is modified and returned, same for the TEST structure.
    _rpcrecv_int(Socket,&xdr_buffer,&ans);
    _rpcrecv_TEST(Socket,&xdr_buffer,&Test);
    printf("Message sent to server : %d\n",t);
    printf("Server answered        : %d\n",ans);
    printf("New TEST               : %d %d %d %g %lg\n",Test.i1,Test.i2,Test.i3,Test.f,Test.d);

// OK ! seems to work, bye !
    _StopTCPClient(Socket,&rpc_msg,&xdr_buffer);
}

//-----------------------------------------------------------------------------
// udp client implementation
//-----------------------------------------------------------------------------
void mainUDP (char *service)

{
    SOCKET udpSocket;
    sockaddr_in bcast_addr,src_addr;
    long int udpQuery;
    int end,stat,len;
    struct hostent *host;
    time_t t1,t2;
    struct timeval timeout={0,0};
    char sz_info[256];
    struct {
        long int udpResponse;
        char szb[128];
    } udp;

// Creation of the socket corresponding to the specified service
    udpSocket=_StartUDPClient(service,10,&bcast_addr);
    if (udpSocket==0) {
        _GetErrMsg(sz_info);
        printf("%s\n",sz_info);
        return;
    }

// The udp message is 32 bits long, the value is just 1 in this example
    udpQuery=htonl(1);
    stat=sendto(udpSocket,(char *)&udpQuery,sizeof(long int),0,
                (const struct sockaddr *)&bcast_addr,sizeof(sockaddr_in));
    if (stat!=sizeof(long int)) {
        _StopUDPClient(udpSocket);
        return;
    }

// Now I am waiting for answers coming from the different servers, max. delay 10 seconds
    time(&t1);
    end=0;
    do {
        switch (_socket_ready_r(udpSocket,&timeout)) {
        case -1:                    // this is an error !
            end=1;
            break;
        case 0:                     // no error, socket not ready !
            time(&t2);
            end=(t2-t1)>10;
            break;
        default:                    // no error, socket ready !
            len=sizeof(src_addr);
            stat=recvfrom(udpSocket,(char *)&udp,sizeof(udp),0,
                          (struct sockaddr *)&src_addr,&len);
            if (stat==sizeof(udp)) {
                udp.udpResponse=ntohl(udp.udpResponse);
                switch (udp.udpResponse) {
                case 2:                             // the server answers 2 (1+udpQuery) : good !
                    strcpy(sz_info,udp.szb);
                    break;
                default:
                    strcpy(sz_info,"");
                }
                if (strlen(sz_info)!=0) {
                    host=gethostbyaddr((char *)&src_addr.sin_addr,sizeof(struct in_addr),PF_INET);
                    if (host!=NULL) printf("%s : ",host->h_name);
                    else            printf("%s : ",inet_ntoa(src_addr.sin_addr));
                    printf("%s\n",sz_info);
                }
            }
        }
    } while (!end);

    _StopUDPClient(udpSocket);
}

//-----------------------------------------------------------------------------
// main function
//-----------------------------------------------------------------------------
int main (int argc, char **argv)

{

    if (argc!=3) {
        printf("usage : %s <server> <service> for tcp test\n",argv[0]);
        printf("usage : %s - <service>        for udp test\n",argv[0]);
        return 0;
    }

    if (strcmp(argv[1],"-")==0) mainUDP(argv[2]);
    else                        mainTCP(argv[1],argv[2]);

    return 0;
}
 


Description of the XDR functions
 

The XDR functions all have the same structure :

#define XDR_ENCODE 1
#define XDR_DECODE 2
#define XDR_MEMORY 3

The different functions are :
 
for int variables :  size_t _xdr_int (void *h, void *n, int code);
for unsigned int variables :  size_t _xdr_uint (void *h, void *n, int code);
for short int variables :  size_t _xdr_short (void *h, void *n, int code);
for long int variables :  size_t _xdr_long (void *h, void *n, int code);
for char variables :  size_t _xdr_char (void *h, void *n, int code);
for float variables :  size_t _xdr_float (void *h, void *n, int code);
for double variables :  size_t _xdr_double (void *h, void *n, int code);
From these functions, you can define your own XDR functions adapted to some more complex structures (see the server/client examples above).
 


Description of the send/receive functions
 

size_t _rpcsend_buffer (SOCKET Socket,
                        RPC_MSG *rpc_msg,
                        XDR_BUFFER *xdr_buffer,
                        void *Buffer,
                        size_t (*xdr_func)(void *, void *, int));

This function sends the content of the specified buffer to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    Buffer      : points to the buffer to be sent
    xdr_func    : address of the user defined XDR routine

Return value    : 0 if an error occurs
                : >0 if successful (number of bytes sent)


size_t _rpcrecv_buffer (SOCKET Socket,
                        XDR_BUFFER *xdr_buffer,
                        void *Buffer,
                        size_t (*xdr_func)(void *, void *, int));

This function receives data from a socket a stores it to the specified buffer. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    Buffer      : points to the buffer to receive
    xdr_func    : address of the user defined XDR routine

Return value    : 0 if an error occurs
                : >0 if successful (number of bytes received)


size_t _rpcsend_buffer (SOCKET Socket,
                        RPC_MSG *rpc_msg,
                        char *Buffer,
                        size_t Len);

This function sends the content of the specified buffer to a socket, without XDR conversion. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    Buffer      : points to the buffer to be sent
    Len         : number of bytes to be sent

Return value    : 0 if an error occurs
                : >0 if successful (number of bytes sent)


size_t _rpcrecv_buffer (SOCKET Socket,
                        char *Buffer,
                        size_t Len);

This function receives data from a socket a stores it to the specified buffer, without XDR conversion. The parameters are :

    Socket      : identifies the socket from which the data must be read
    Buffer      : points to the buffer to receive
    Len         : number of bytes to receive

Return value    : 0 if an error occurs
                : >0 if successful (number of bytes received)


int _rpc_flush (SOCKET Socket,
                RPC_MSG *rpc_msg);

To avoid some lost of time during transfer of the information through the network, the data to be sent are first stored to an internal buffer. Once this buffer is full, it is automatically flushed, thus resulting in an effective transmission to the destination socket.
This function can be called at any moment, it allows to force flushing and effective transmission to the destination socket. Using this function is required if the total amount of data to transfer is less than the internal buffer size (8192 bytes). The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering

Return value    : 0 if an error occurs
                : >0 if successful (number of bytes received)


int _rpcsend_int (SOCKET Socket,
                  RPC_MSG *rpc_msg,
                  XDR_BUFFER *xdr_buffer,
                  int I);

This function sends a int to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    I           : the int value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_int (SOCKET Socket,
                  XDR_BUFFER *xdr_buffer,
                  int *I);

This function receives a int from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    I           : points to the int value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_uint (SOCKET Socket,
                   RPC_MSG *rpc_msg,
                   XDR_BUFFER *xdr_buffer,
                   unsigned int I);

This function sends a uint to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    I           : the uint value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_uint (SOCKET Socket,
                   XDR_BUFFER *xdr_buffer,
                   unsigned int *I);

This function receives a uint from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    I           : points to the uint value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_long (SOCKET Socket,
                   RPC_MSG *rpc_msg,
                   XDR_BUFFER *xdr_buffer,
                   long I);

This function sends a long to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    I           : the long value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_long (SOCKET Socket,
                   XDR_BUFFER *xdr_buffer,
                   long *I);

This function receives a long from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    I           : points to the long value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_short (SOCKET Socket,
                    RPC_MSG *rpc_msg,
                    XDR_BUFFER *xdr_buffer,
                    short I);

This function sends a short to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    I           : the short value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_short (SOCKET Socket,
                    XDR_BUFFER *xdr_buffer,
                    short *I);

This function receives a short from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    I           : points to the short value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_char (SOCKET Socket,
                   RPC_MSG *rpc_msg,
                   XDR_BUFFER *xdr_buffer,
                   char C);

This function sends a char to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    C           : the char value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_char (SOCKET Socket,
                   XDR_BUFFER *xdr_buffer,
                   char *C);

This function receives a char from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    C           : points to the char value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_string (SOCKET Socket,
                     RPC_MSG *rpc_msg,
                     char *S,
                     size_t SLen);

This function sends a character string to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    S           : points to the character string to be sent
    SLen        : length of S

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_string (SOCKET Socket,
                     char *S,
                     size_t SLen);

This function receives a character string from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    S           : points to the character string to be read
    SLen        : length of S

Return value    : 0 if an error occurs
                : 1 if successful

Warning : this function assumes that the length of the character string is known !


int _rpcsend_float (SOCKET Socket,
                    RPC_MSG *rpc_msg,
                    XDR_BUFFER *xdr_buffer,
                    float X);

This function sends a float to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    X           : the float value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_float (SOCKET Socket,
                    XDR_BUFFER *xdr_buffer,
                    float *X);

This function receives a float from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    X           : points to the float value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_double (SOCKET Socket,
                     RPC_MSG *rpc_msg,
                     XDR_BUFFER *xdr_buffer,
                     double X);

This function sends a double to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    xdr_buffer  : points to a user structure for XDR buffering
    X           : the double value to be sent

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcrecv_double (SOCKET Socket,
                     XDR_BUFFER *xdr_buffer,
                     double *X);

This function receives a double from a socket. The parameters are :

    Socket      : identifies the socket from which the data must be read
    xdr_buffer  : points to a user structure for XDR buffering
    X           : points to the double value to be read

Return value    : 0 if an error occurs
                : 1 if successful


int _rpcsend_tabfloat (SOCKET Socket,
                       RPC_MSG *rpc_msg,
                       float *X,
                       long int N,
                       int RestoreData);

This function sends a series of float to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    X           : points to the series of float to be sent
    N           : number of float values to be sent
    RestoreData : specifies if the data must be restored or not

Return value    : 0 if an error occurs
                : 1 if successful

Remarks :


int _rpcrecv_tabfloat (SOCKET Socket,
                       float *X,
                       long int N);

This function reads a series of float from a socket. The parameters are :

    Socket      : identifies the destination socket
    X           : points to the series of float to be read
    N           : number of float values to be read

Return value    : 0 if an error occurs
                : 1 if successful

Warning : this function assumes that the number of values to be read is known !


int _rpcsend_tabdouble (SOCKET Socket,
                        RPC_MSG *rpc_msg,
                        double *X,
                        long int N,
                        int RestoreData);

This function sends a series of double to a socket. The parameters are :

    Socket      : identifies the destination socket
    rpc_msg     : points to a user structure for internal buffering
    X           : points to the series of double to be sent
    N           : number of double values to be sent
    RestoreData : specifies if the data must be restored or not

Return value    : 0 if an error occurs
                : 1 if successful

Remarks :


int _rpcrecv_tabdouble (SOCKET Socket,
                        double *X,
                        long int N);

This function reads a series of double from a socket. The parameters are :

    Socket      : identifies the destination socket
    X           : points to the series of double to be read
    N           : number of double values to be read

Return value    : 0 if an error occurs
                : 1 if successful

Warning : this function assumes that the number of values to be read is known !


int _socket_ready_r (SOCKET Socket,
                     struct timeval *timeout);

This function determines if a socket is ready for reading. The parameters are :

    Socket      : identifies the socket
    timeout     : points to a struct timeval specifying the timeout

Return value    : -1 if an error occurs
                :  0 if no error occurs and the socket is not ready for reading
                :  1 if no error occurs and the socket is ready for reading

Remark : timeout can be NULL, in such case, this function blocks until an event occurs ; it can be also set as {0,0} to specify an immediate return of the function.


int _socket_ready_w (SOCKET Socket,
                     struct timeval *timeout);

This function determines if a socket is ready for writting. The parameters are :

    Socket      : identifies the socket
    timeout     : points to a struct timeval specifying the timeout

Return value    : -1 if an error occurs
                :  0 if no error occurs and the socket is not ready for writting
                :  1 if no error occurs and the socket is ready for writting

Remark : timeout can be NULL, in such case, this function blocks until an event occurs ; it can be also set as {0,0} to specify an immediate return of the function.


Additionnal definitions (these are not real functions) :

#define rpcrecv_ulong(S,X,I)    rpcrecv_long(S,X,(long *)I)
#define rpcsend_ulong(S,R,X,I)  rpcsend_long(S,R,X,(long)I)
#define rpcrecv_ushort(S,X,I)   rpcrecv_short(S,X,(short *)I)
#define rpcsend_ushort(S,R,X,I) rpcsend_short(S,R,X,(short)I)
#define rpcrecv_bool            rpcrecv_int
#define rpcsend_bool            rpcsend_int


Description of the server specific functions
 

void _ServerMainLoop (_REG_INFO *RegInfo);

This function initiates the main loop (infinite loop) of any server application. It has only one parameter :

    RegInfo          : a pointer to a _REG_INFO structure (see Software Registration)

Warning : the server must have been started.


int _StartServerDaemon (char *service,
                        int max_timeout,
                        char *logfile,
                        size_t bufsize,
                        void (*Constructor)(void *),
                        void (*Destructor)(void *),
                        void (*Dispatch)(int,int),
                        int  (*AcceptConnection)(char *),
                        void (*udpManager)(SOCKET),
                        _REG_INFO *RegInfo,
                        SOCKET *tcpSocket,
                        SOCKET *udpSocket);

This function starts the server daemon. The parameters are :

    service          : character string identifying the tcp/udp service to use
    max_timeout      : specifies the maximum timeout, in seconds
    logfile          : character string identifying the log file
    bufsize          : specifies the size of an internal buffer associated to each connected client (can be 0)
    Constructor      : this function is called after allocation of the client internal buffer (can be NULL)
    Destructor       : this function is called before desallocation of the client internal buffer (can be NULL)
    Dispatch         : this function is called each time a message is received from the network
    AcceptConnection : this function is called before accepting a connection
    udpManager       : this function is called each time a message is received on the udp socket (if present)
    RegInfo          : a pointer to a _REG_INFO structure (see Software Registration)
    tcpSocket        : points to the socket used for tcp connections
    udpSocket        : points to the socket used for udp connections (can be NULL)

Return value    : 0 if an error occurs
                : 1 if successful


void _StopServerDaemon (SOCKET tcpSocket,
                        SOCKET udpSocket);

This function stops a running server daemon and closes the corresponding sockets. The two parameters are

    tcpSocket   : identifies the socket used for tcp connections
    udpSocket   : identifies the socket used for udp connections


int _GetClientInfo (int idClient,
                    SOCKET *socket,
                    RPC_MSG **rpc_msg,
                    XDR_BUFFER **xdr_buffer,
                    char **client_buffer);

This function retrieves some client specific informations ; it should be the first call of the Dispatch function implemented by the server. The parameters are :

    idClient      : identifies the client (internal use for the server)
    socket        : points to the socket used for communications with the corresponding client
    rpc_msg       : points to the RPC_MSG structure of the client
    xdr_buffer    : points to the XDR_BUFFER structure of the client
    client_buffer : points to the data allocated for each client

Return value    : 0 if an error occurs
                : 1 if successful


Description of the client specific functions
 

SOCKET _StartTCPClient (char *servername,
                        char *service,
                        int max_timeout,
                        RPC_MSG *rpc_msg,
                        XDR_BUFFER *xdr_buffer);

This function initiates the client-side of the communication with a server on the network (tcp connection). The parameters are :

    servername    : character string identifying the name of the server to use
    service       : character string identifying the tcp service to use
    max_timeout   : specifies the maximum timeout, in seconds
    rpc_msg       : points to the RPC_MSG structure of the client
    xdr_buffer    : points to the XDR_BUFFER structure of the client

Return value    : 0 if an error occurs
                : >0 if successful (socket identification)


SOCKET _StartUDPClient (char *service,
                        int max_timeout,
                        sockaddr_in *bcast_addr);

This function initiates the client-side of the communication with a server on the network (udp connection). The parameters are :

    service       : character string identifying the udp service to use
    max_timeout   : specifies the maximum timeout, in seconds
    bcast_addr    : points to the broadcast address (can be NULL)

Return value    : 0 if an error occurs
                : >0 if successful (socket identification)


int _StopTCPClient (SOCKET Socket,
                    RPC_MSG *rpc_msg,
                    XDR_BUFFER *xdr_buffer);

This function stops a tcp client. The parameters are :

    Socket        : identifies the communication socket
    rpc_msg       : points to the RPC_MSG structure of the client
    xdr_buffer    : points to the XDR_BUFFER structure of the client

Return value    : 0 if an error occurs
                : >0 if successful (socket identification)


void _StopUDPClient (SOCKET Socket);

This function stops a udp client. The parameters are :

    Socket        : identifies the communication socket


Description of the common functions
 

void _GetErrMsg (char *msg);

This function copies to the character string msg the last error message.


int _rpc_gethostname (char *hostname,
                      int maxlen,
                      char *ipaddr);

This function determines the ip address of the specified host identified by its name hostname.
If hostname is an empty character string, this function also returns the ip name of the local computer ; in such case the maximum length of the variable is required (maxlen). In any other cases, this parameter is ignored.
The character string ipaddr is returned by the function, it contains the ip address of the local computer/specified host. The character string must be declared as an array of minimum length 16.

The function returns 1 in case of success, 0 if an error occurs.